package de.g18.it0a.jlaser.gui.controller;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.FileNotFoundException;
import java.io.IOException;

import javax.swing.JFileChooser;
import javax.swing.JOptionPane;
import javax.swing.Timer;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;

import de.g18.it0a.jlaser.dataio.RoomConfigurationReader;
import de.g18.it0a.jlaser.dataio.RoomConfigurationWriter;
import de.g18.it0a.jlaser.domain.LaserConfiguration;
import de.g18.it0a.jlaser.domain.Room;
import de.g18.it0a.jlaser.domain.RoomConfiguration;
import de.g18.it0a.jlaser.exception.InitializationException;
import de.g18.it0a.jlaser.exception.JLFMalformedFileException;
import de.g18.it0a.jlaser.exception.JLFParseException;
import de.g18.it0a.jlaser.exception.MalformedConfigurationException;
import de.g18.it0a.jlaser.factory.RoomFactory;
import de.g18.it0a.jlaser.gui.view.ExtensionFileFilter;
import de.g18.it0a.jlaser.gui.view.MainView;
import de.g18.it0a.jlaser.jlf.JLFConverter;
import de.g18.it0a.jlaser.solution.BruteforceSolutionManager;
import de.g18.it0a.jlaser.solution.SolutionManager;
import de.g18.it0a.jlaser.solution.SolutionStepListener;


public class MainViewController {

    private final MainView view;

    private boolean debugEnabled;
    private boolean roomUpdating;
    private Timer jlfTimer;


    public MainViewController() {
        view = new MainView();
        view.addFileListSelectionChangeHandler(new RoomFileSelectionChangeListener());
        view.addSolveButtonActionListener(new CalculateMirrorPositionsAction());
        view.addJLFEditorListener(new JLFEditorListener());
        view.addSaveActionListener(new SaveActionListener());
        view.addDebugListener(new DebugListener());
        debugEnabled = view.isDebugEnabled();
    }


    // Inner Classes ***********************************************************

    private class RoomFileSelectionChangeListener implements ActionListener {

        public RoomFileSelectionChangeListener() {
            changeSelection();
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            changeSelection();
        }

        private void changeSelection() {
            if (roomUpdating) {
                return;
            }
            roomUpdating = true;

            try {
                String selection = view.getSelectedFile();
                view.setRoom(null);
                view.setJLFEditorText("");

                if (selection.isEmpty()) {
                    view.setSolveButtonEnabled(false);
                    return;
                } else if ("Durchsuchen...".equals(selection)) {
                    JFileChooser fileChooser = new JFileChooser(".");
                    fileChooser.addChoosableFileFilter(new ExtensionFileFilter("JLaser Format (*.jlf)", "jlf"));
                    fileChooser.showOpenDialog(view);
                    if (fileChooser.getSelectedFile() == null) {
                        view.setSolveButtonEnabled(false);
                        return;
                    }
                    selection = fileChooser.getSelectedFile().getAbsolutePath();

                    view.addItemToFileList(selection);
                    view.setSelectedFile(selection);
                }

                try {
                    RoomConfiguration config = RoomConfigurationReader.read(selection);
                    view.setRoom(RoomFactory.buildRoom(config));
                    view.setJLFEditorText(JLFConverter.valueOf(config));
                    view.setSolveButtonEnabled(true);
                } catch (FileNotFoundException e) {
                    JOptionPane.showMessageDialog(view, "Die Datei '" + selection + "' konnte nicht gefunden werden!");
                } catch (JLFMalformedFileException e) {
                    JOptionPane.showMessageDialog(view, "Die Datei '" + selection + "' enthält keine gültigen Daten!");
                } catch (MalformedConfigurationException e) {
                    JOptionPane.showMessageDialog(view, "Die Datei '" + selection + "' enthält keine gültigen Daten!");
                }
            } finally {
                roomUpdating = false;
            }
        }
    }

    private class CalculateMirrorPositionsAction implements ActionListener {
        @Override
        public void actionPerformed(ActionEvent aEvent) {
            new Thread(new SolutionTask()).start();
        }
    }

    private class SolutionTask implements Runnable {
        @Override
        public void run() {
            if (roomUpdating) {
                return;
            }
            roomUpdating = true;

            try {
                view.setSolveButtonEnabled(false);
                view.setCalculating(true);
                SolutionManager solutionManager = new BruteforceSolutionManager(view.getRoom().getConfig());
                solutionManager.addSolutionStepListener(new SolutionManagerDebugListener());
                LaserConfiguration solution = solutionManager.solve();

                try {
                    Room room = null;
                    if (solution != null) {
                        room = RoomFactory.buildRoom(view.getRoom().getConfig(), solution);
                    } else {
                        room = RoomFactory.buildRoom(view.getRoom().getConfig());
                        JOptionPane.showMessageDialog(view, "Für den Raum konnte keine Lösung gefunden werden!");
                    }
                    view.setRoom(room);
                } catch (MalformedConfigurationException e) {
                    throw new IllegalArgumentException("Corrupt configuration found its way into the room editor, this should be impossible!!!", e);
                }
                view.setCalculating(false);
                view.setSolveButtonEnabled(true);
            } finally {
                roomUpdating = false;
            }
        }
    }

    private class JLFEditorListener implements DocumentListener {

        public JLFEditorListener() {
            jlfTimer = new Timer(1500, new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    updateRoomEditorPanel();
                }
            });
        }

        private void updateRoomEditorPanel() {
            jlfTimer.stop();
            if (roomUpdating) {
                return;
            }

            view.setSolveButtonEnabled(false);
            try {
                RoomConfiguration config = JLFConverter.valueOf(view.getJLFEditorText());
                view.setRoom(RoomFactory.buildRoom(config));
                view.setSolveButtonEnabled(true);
            } catch (InitializationException e) {
                // TODO Warnung ausgeben!
                System.err.println(e);
            } catch (JLFParseException e) {
                // TODO Warnung ausgeben!
                System.err.println(e);
            } catch (MalformedConfigurationException e) {
                // TODO Warnung ausgeben!
                System.err.println(e);
            }
        }

        @Override
        public void removeUpdate(DocumentEvent e) {
            resetTimer();
        }

        @Override
        public void insertUpdate(DocumentEvent e) {
            resetTimer();
        }

        @Override
        public void changedUpdate(DocumentEvent e) {
            resetTimer();
        }

        private void resetTimer() {
            jlfTimer.stop();

            toggleSaveActionEnablement();
            if (!roomUpdating) {
                jlfTimer.start();
            }
        }

        private void toggleSaveActionEnablement() {
            view.setSaveActionEnabled(view.getJLFEditorText() != null && !view.getJLFEditorText().isEmpty());
        }
    }

    private class SaveActionListener implements ActionListener {
        @Override
        public void actionPerformed(ActionEvent e) {
            JFileChooser fileChooser = new JFileChooser(".");
            fileChooser.addChoosableFileFilter(new ExtensionFileFilter("JLaser Format (*.jlf)", "jlf"));
            fileChooser.showSaveDialog(view);
            if (fileChooser.getSelectedFile() != null) {
                String filePath = fileChooser.getSelectedFile().getAbsolutePath();
                try {
                    RoomConfigurationWriter.write(view.getRoom().getConfig(), filePath);
                } catch (IOException e1) {
                    JOptionPane.showMessageDialog(view, "Fehler beim speichern des Raumes!\n" + e1.getLocalizedMessage());
                }
                view.addItemToFileList(filePath);
                view.setSelectedFile(filePath);
            }
        }
    }

    private class SolutionManagerDebugListener implements SolutionStepListener {
        @Override
        public void stepPerformed(LaserConfiguration aLaserConfig) {
            if (!debugEnabled) {
                return;
            }

            try {
                view.setRoom(RoomFactory.buildRoom(view.getRoom().getConfig(), aLaserConfig));
            } catch (MalformedConfigurationException e) {
                throw new IllegalStateException("Either the Room, or the laser configuration was corrupt!\n" + e.getLocalizedMessage(), e);
            }
        }
    }

    private class DebugListener implements ActionListener {
        @Override
        public void actionPerformed(ActionEvent e) {
            debugEnabled = view.isDebugEnabled();
        }
    }
}
